package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.IdAndNumDTO;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author XinxuanZhuo
 * @since 2024-02-05
 */

@SuppressWarnings("ALL")
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    // 课程服务接口
    private final CourseClient courseClient;

    // 课程小节接口
    private final CatalogueClient catalogueClient;

    // 课程记录mapper
    private final LearningRecordMapper recordMapper;

    /**
     * 接收到Mq通知后，添加课程到我的课表
     */
    @Override
    public void addUserLessons(Long userId, List<Long> courseIds) {
        // 1.远程调用课程微服务，获取课程有效期
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cInfoList)) {
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        // 2.循环遍历，处理LearningLesson数据
        List<LearningLesson> resultList = new ArrayList<>();
        for (CourseSimpleInfoDTO cInfo : cInfoList) {
            LearningLesson lesson = new LearningLesson();
            // 获取过期时间, 当前时间加上课程有效期
            Integer validDuration = cInfo.getValidDuration();
            // 只有当当前课程有效期合法才设置过期时间，毕竟有些课程永久
            if (validDuration != null && validDuration > 0) {
                LocalDateTime now = LocalDateTime.now();
                lesson.setCreateTime(now);
                lesson.setExpireTime(now.plusMonths(validDuration));
            }
            // 填充userId和courseId
            lesson.setCourseId(cInfo.getId());
            lesson.setUserId(userId);
            resultList.add(lesson);
        }
        // 3.批量新增
        saveBatch(resultList);
    }

    /**
     * 分页查询我的课表
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        // 1.获取当前用户
        Long userId = UserContext.getUser();
        // 2.分页查询
        Page<LearningLesson> page = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        // 3.远程调用course-service
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        Map<Long, CourseSimpleInfoDTO> cMap = queryCourseSimpleInfoList(records);

        // 4.封装vo
        List<LearningLessonVO> list = new ArrayList<>(records.size());
        // 4.1.循环遍历，把LearningLesson转为VO
        for (LearningLesson r : records) {
            // 4.2.拷贝基础属性到vo
            LearningLessonVO vo = BeanUtils.copyBean(r, LearningLessonVO.class);
            // 4.3.获取课程信息，填充到vo
            CourseSimpleInfoDTO cInfo = cMap.get(r.getCourseId());
            vo.setCourseName(cInfo.getName());
            vo.setCourseCoverUrl(cInfo.getCoverUrl());
            vo.setSections(cInfo.getSectionNum());
            list.add(vo);
        }
        return PageDTO.of(page, list);
    }


    /**
     * 查询用户当前正在学习的课程
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        // 1.获取用户ID
        Long userId = UserContext.getUser();
        // 2.查询正在学习的课程 select * from xx where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (lesson == null) {
            return null;
        }
        // 3.拷贝PO基础属性到VO
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        // 4.远程调用course-service获取课程信息
        CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(vo.getCourseId(), false, false);
        if (cInfo == null) {
            throw new BadRequestException("课程不存在");
        }
        vo.setCourseName(cInfo.getName());
        vo.setCourseCoverUrl(cInfo.getCoverUrl());
        vo.setSections(cInfo.getSectionNum());
        // 5.统计课表中的课程数量 select count(1) from xxx where user_id = #{userId}
        Integer courseAmount = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();
        vo.setCourseAmount(courseAmount);
        // 6.查询小节信息
        List<CataSimpleInfoDTO> cataInfos =
                catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));
        if (!CollUtils.isEmpty(cataInfos)) {
            CataSimpleInfoDTO cataInfo = cataInfos.get(0);
            vo.setLatestSectionName(cataInfo.getName());
            vo.setLatestSectionIndex(cataInfo.getCIndex());
        }
        return vo;
    }

    /**
     * 接收到Mq退款通知后，将课程从我的课表中删除
     */
    @Override
    public void deleteUserLesson(Long userId, List<Long> courseIds) {
        log.info("删除用户:{}退款的课程课表:{}", userId, courseIds);
        // 根据用户名和课程名查询课表
        List<Long> lIds = new ArrayList<>();
        for (Long cid : courseIds) {
            LearningLesson lesson = lambdaQuery()
                    .eq(LearningLesson::getUserId, userId)
                    .eq(LearningLesson::getCourseId, cid)
                    .one();
            if (lesson != null) {
                lIds.add(lesson.getId());
            }
        }
        if (CollUtils.isEmpty(lIds)) {
            log.error("课表不存在或者已删除，检查课程信息");
            return;
        }
        // 根据id批量删除课表
        removeByIds(lIds);
    }

    /**
     * 根据课程id，检查当前用户的课表中是否有该课程，课程状态是否有效。
     */
    @Override
    public Long isLessonValid(Long courseId) {
        // 获取当前登录用户
        Long userId = UserContext.getUser();
        // 根据用户id和课程id查询数据库，查找课表，课程状态是否有效
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        // 不存在返回null 或者课程过期返回null
        if (lesson == null ||
                (lesson.getExpireTime() != null && lesson.getExpireTime().isBefore(LocalDateTime.now()))) {
            return null;
        }
        //存在返回课表id （lessonId）
        return lesson.getId();
    }

    /**
     * 根据课程id，查询当前用户的课表中是否有该课程，
     * 如果有该课程则需要返回课程的学习进度、课程有效期等信息。
     */
    @Override
    public LearningLessonVO queryLessonByCourseId(Long courseId) {
        // 获取用户id
        Long userId = UserContext.getUser();
        // 根据用户id和课程id查询数据库
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (lesson == null) {
            return null;
        }
        // po转vo返回
        return BeanUtils.copyBean(lesson, LearningLessonVO.class);
    }

    /**
     * 统计课程学习人数
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        Integer count = lambdaQuery().eq(LearningLesson::getCourseId, courseId).count();
        if (count == null) {
            throw new BizIllegalException("课程id有误");
        }
        return count;
    }

    /**
     * 根据用户id和课程id查询课表
     */
    @Override
    public LearningLesson queryByUserIdAndCourseId(Long userId, Long courseId) {
        return getOne(this.buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    /**
     * 创建学习计划
     */
    @Override
    public void createLearningPlan(Long courseId, Integer freq) {
        // 1.获取当前登录的用户
        Long userId = UserContext.getUser();
        // 2.查询课表中的指定课程有关的数据
        LearningLesson lesson = queryByUserIdAndCourseId(userId, courseId);
        AssertUtils.isNotNull(lesson, "课程信息不存在！");
        // 3.修改数据
        LearningLesson targetLesson = new LearningLesson();
        targetLesson.setId(lesson.getId());
        targetLesson.setWeekFreq(freq);
        if (lesson.getPlanStatus() == PlanStatus.NO_PLAN) {
            targetLesson.setPlanStatus(PlanStatus.PLAN_RUNNING);
        }
        updateById(targetLesson);
    }

    /**
     * 查寻学习计划进度
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        LearningPlanPageVO result = new LearningPlanPageVO();
        // 获取当前用户
        Long userId = UserContext.getUser();
        // 获取本周起始时间
        LocalDate now = LocalDate.now();
        LocalDateTime begin = DateUtils.getWeekBeginTime(now);
        LocalDateTime end = DateUtils.getWeekEndTime(now);
        // 查询本周已学习课程总数
        Integer weekFinished = recordMapper.selectCount(
                new LambdaQueryWrapper<LearningRecord>()
                        .eq(LearningRecord::getUserId, userId)
                        .eq(LearningRecord::getFinished, true)
                        .between(LearningRecord::getFinishTime, begin, end)
        );
        result.setWeekFinished(weekFinished);
        // 查询本周需要学习小节总数
        Integer weekTotalPlan = baseMapper.queryWeekPlanTotal(userId);
        result.setWeekTotalPlan(weekTotalPlan);
        // TODO 查询本周学习积分
        // 查询分页数据
        // 分页查询课表信息以及学习计划信息
        Page<LearningLesson> learningPageInfo = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> learningRecords = learningPageInfo.getRecords();
        if (CollUtils.isEmpty(learningRecords)){
            return result.emptyPage(learningPageInfo);
        }

        // 查询课表里面课程的信息(远程调用课程服务)
        Map<Long, CourseSimpleInfoDTO> cMap = queryCourseSimpleInfoList(learningRecords);
        // 统计每一个课程本周已学习小节数量
        List<IdAndNumDTO> list = recordMapper.countLearnedSections(userId, begin, end);
        // key是课程id，值是本周学习小节数
        Map<Long, Integer> countMap = IdAndNumDTO.toMap(list);
        // 组装LearningPlanVO
        List<LearningPlanVO> voList = new ArrayList<>(learningRecords.size());
        for (LearningLesson r : learningRecords) {
            // 拷贝基础属性到vo
            LearningPlanVO vo = BeanUtils.copyBean(r, LearningPlanVO.class);
            // 填充课程详细信息
            CourseSimpleInfoDTO cInfo = cMap.get(r.getCourseId());
            if (cInfo != null) {
                vo.setCourseName(cInfo.getName());
                vo.setSections(cInfo.getSectionNum());
            }
            // 每个课程的本周已学习小节数量
            vo.setWeekLearnedSections(countMap.getOrDefault(r.getId(), 0).intValue());
            voList.add(vo);
        }
        return result.pageInfo(learningPageInfo.getTotal(), learningPageInfo.getPages(), voList);
    }

    /**
     * 远程调用course-service，将查询信息包装成map
     * key是课程id，值是课程实体
     */
    private Map<Long, CourseSimpleInfoDTO> queryCourseSimpleInfoList(List<LearningLesson> records) {
        // 1.获取所有课程id
        Set<Long> cIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        // 2.doAction
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(cIds);
        if (CollUtils.isEmpty(cInfoList)) {
            // 课程不存在，无法添加
            throw new BadRequestException("课程信息不存在！");
        }
        // 3.把课程集合处理成Map，key是courseId，值是course本身
        return cInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, course -> course));
    }

    /**
     * 根据用户id和课程id build查询课表的wrapper
     */
    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }
}
